package com.hzb.LNReader;

import java.io.File;
import java.io.IOException;
import java.util.concurrent.Semaphore;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.DialogInterface.OnClickListener;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.MeasureSpec;
import android.widget.FrameLayout;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.SeekBar.OnSeekBarChangeListener;

import com.hzb.LNReader.view.ReaderTouchListener;
import com.hzb.LNReader.view.ReaderView;

public class ReaderActivity extends Activity {
	public static final String LOG = "ReaderActivity";
	public static final String DST_FILE = "dstFile";
	public static final String CONFIG = "config";
	private static boolean isFirstLoad = true;
	private FileReader fileReader;
	ReaderView view;
	public ReadHistory currReadFile;
	boolean isOpen = false;
	Object statusLock = new Object();
	private TextView controlProcessText;
	private String oriProcess = "";
	private int dotIndex = -1;
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		MenuInflater inflater = this.getMenuInflater();
		inflater.inflate(R.menu.readermenu, menu);
		return true;
	}

	public void onNumKeyPress(View v) {
		boolean goSeek = false;
		switch (v.getId()) {
		case R.id.num0:
			if (oriProcess.length() > 0) {
				oriProcess += "0";
			}
			break;
		case R.id.num1:
			oriProcess += "1";
			break;
		case R.id.num2:
			oriProcess += "2";
			break;
		case R.id.num3:
			oriProcess += "3";
			break;
		case R.id.num4:
			oriProcess += "4";
			break;
		case R.id.num5:
			oriProcess += "5";
			break;
		case R.id.num6:
			oriProcess += "6";
			break;
		case R.id.num7:
			oriProcess += "7";
			break;
		case R.id.num8:
			oriProcess += "8";
			break;
		case R.id.num9:
			oriProcess += "9";
			break;
		case R.id.numDot:
			if (dotIndex != -1) return;
			dotIndex = oriProcess.length();
			if (oriProcess.length() > 0) {
				oriProcess += ".";
			} else {
				oriProcess = "0.";
			}
			
			break;
		case R.id.numX:
			findViewById(R.id.processControllPanel).setVisibility(View.INVISIBLE);
			return;
		case R.id.numCls:
			dotIndex = -1;
			oriProcess = "";
			break;
		case R.id.numOk:
			goSeek = true;
			break;
		case R.id.numBk:
			int len = oriProcess.length();
			if (len > 0) {
				if (oriProcess.charAt(len - 1) == '.') dotIndex = -1;
				oriProcess = oriProcess.substring(0, len - 1);
			}
			break;
		}
		if (dotIndex == -1) {
			if (oriProcess.length() > 2) oriProcess = oriProcess.substring(1);
		} else {
			if (oriProcess.length() > dotIndex + 2) oriProcess = oriProcess.substring(0, dotIndex + 2);
		}
		float pro = updateControlProcess();
		if (goSeek) {
			try {
				seekTo(pro);
			} catch (IOException e) {
				showError();
			}
		}
	}
	
	private float updateControlProcess() {
		float pro = 0;
		if (oriProcess.length() != 0) {
			controlProcessText.setText(oriProcess);
			pro = Float.parseFloat(oriProcess);
			seekBar.setProgress((int)pro);
		} else {
			controlProcessText.setText("0");
			seekBar.setProgress(0);
		}
		return pro;
	}
	
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		int id = item.getItemId();
		switch (id) {
		case R.id.openFile:
			openFileSelector();
			return true;
		case R.id.showHistory: {
			Intent intent = new Intent(this, ShowHistoryActivity.class);
			startActivity(intent);
			close();
			finish();
			return true;
		}
		case R.id.showSeekPanel:
			findViewById(R.id.processControllPanel).setVisibility(View.VISIBLE);
			int[] displayInfo = view.getPosition();
			oriProcess = Integer.toString(displayInfo[2] / 10);
			dotIndex = -1;
			updateControlProcess();
			break;
			// 修改编码
		case R.id.gbk:
			if (id == R.id.gbk)
				currReadFile.encoding = "GBK";
		case R.id.utf8:
			if (id == R.id.utf8)
				currReadFile.encoding = "UTF-8";
		case R.id.big5:
			if (id == R.id.big5)
				currReadFile.encoding = "BIG-5";
			openFile(null);
			return true;
		case R.id.setting: {
			Intent intent = new Intent(this, SettingActivity.class);
			startActivity(intent);
			close();
			finish();
			return true;
		}
		case R.id.exit:
			exit();
			return true;
		}
		return false;
	}

	public final void openFile(String newFileName) {
		File dstFile;
		if (newFileName != null) {
			File newFile = new File(newFileName);
			if (!newFile.isFile()) {
				openFileSelector();
				return;
			}
			dstFile = newFile;
			currReadFile = ReaderContext.openReadHistory(dstFile);
		} else {
			for (;;) {
				if (ReaderContext.head == null) {
					openFileSelector();
					return;
				}
				currReadFile = ReaderContext.head;
				if (currReadFile.file.isFile()) {
					dstFile = currReadFile.file;
					break;
				}
				currReadFile.remove();
			}
		}

		view = new ReaderView(this);
		view.setOnTouchListener(new ReaderTouchListener(view));
		frame.removeAllViews();
		frame.addView(view);;
		fileReader = new FileReader();
		isOpen = true;
		fillTask = new FillContentTask();
		fillTask.execute();
		
	}

	public final void openFileSelector() {
		Intent intent = new Intent(this, FileSelectActivity.class);
		startActivity(intent);
		close();
		finish();
	}

	private View contentView;
	private FrameLayout frame;
	private SeekBar seekBar;
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.readerlayout);
		contentView = findViewById(R.id.ReaderLayout);
		controlProcessText = (TextView) findViewById(R.id.controlProcess);
		seekBar = (SeekBar) findViewById(R.id.SeekBar);
		seekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
			@Override
			public void onStopTrackingTouch(SeekBar seekBar) {}
			@Override
			public void onStartTrackingTouch(SeekBar seekBar) {}
			@Override
			public void onProgressChanged(SeekBar seekBar, int progress,
					boolean fromUser) {
				if (fromUser) {
					oriProcess = Integer.toString(progress);
					controlProcessText.setText(oriProcess);
				}
			}
		});
		frame = (FrameLayout) findViewById(R.id.FrameLayout);
		if (isFirstLoad) {
			ReaderContext.loadFrom(getFileStreamPath(CONFIG));
			
			isFirstLoad = false;
		}
		openFile(getIntent().getStringExtra(DST_FILE));
	}
	
	@Override
	protected void onResume() {
		super.onResume();
		setContentView(contentView);
	}

	public void fillConent(boolean isAppend) {
		if (fillTask == null && isOpen && seekTask == null) {
			FillContentTask fillTask = new FillContentTask();
			this.fillTask = fillTask;
			fillTask.execute(isAppend);
		}
	}
	class SeekTask extends AsyncTask<Float, Void, Integer> {	
		@Override
		protected Integer doInBackground(Float... params) {
			FillContentTask task = fillTask;
			if (task != null) {
				synchronized (task) {
					if (fillTask != null) {
						try {
							task.wait();
						} catch (InterruptedException e) {
							Log.e(LOG, "Seek task interrupted");
						}
					}
				}
			}
			synchronized (statusLock) {
				if (!isOpen) return -1;
				try {
					float percent = params[0];
					int offset = (int) (currReadFile.fileSize * percent / 100);
					int lineSeq = fileReader.ensureFileBufferByOffset(offset);
					if (lineSeq + ReaderContext.startFillLine > currReadFile.maxLineSeq) {
						lineSeq = currReadFile.maxLineSeq - ReaderContext.startFillLine;
					}
					return lineSeq;
				} catch (Exception e) {
					Log.e(LOG, "", e);
					return -1;
				}
			}
		}
		
		@Override
		protected void onPostExecute(Integer result) {
			if (result < 0) {
				showError();
				return;
			}
			view.reset(result);
			view.scrollTo(0, 0);
			fillTask = new FillContentTask();
			seekTask = null;
			fillTask.execute(true);
		}
	}
	private SeekTask seekTask;
	public void seekTo(float percent) throws IOException {
		if (seekTask == null) {
			seekTask = new SeekTask();
			seekTask.execute(percent);
		}
		
		
	}
	
	public FillContentTask fillTask;
	
	void showError() {
		if (!isOpen) return;
		ReadHistory tmp = currReadFile;
		close();
		tmp.reset();
		Builder dialog = new AlertDialog.Builder(ReaderActivity.this);
		dialog.setTitle("错误");
		dialog.setMessage("读取文件出错,点击OK重新选择文件。");
		dialog.setPositiveButton("OK", new OnClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int which) {
				openFileSelector();
			}
		});
		dialog.show();
	}
	
	boolean isFirstFill = true;
	Semaphore updateSemaphore = new Semaphore(0);
	
	private class FillContentTask extends
			AsyncTask<Boolean, Void, Integer> {
		
		@Override
		protected void onProgressUpdate(Void... values) {
			if (!isOpen) return;
			view.addPendingHeight();
			updateSemaphore.release();
		}

		@Override
		protected Integer doInBackground(Boolean... params) {
			synchronized (statusLock) {
				if (!isOpen) return 0;
				try {
					if (isFirstFill) {
						if (currReadFile.file.lastModified() != currReadFile.lastModify) {
							currReadFile.reset();
						}
						view.seqFrom = view.seqTo = currReadFile.startLineSeq;
						fileReader.fastLoadFile(currReadFile);
						int ret = view.appendViewsAfter(fileReader.contentLine, fileReader.lineOffset, 0, fileReader.bufferSize);
						publishProgress();
						updateSemaphore.acquire();
						return ret;
					} else {
						boolean isAppend = (Boolean) params[0];
						if (isAppend) {
							int[] fillIndex = fileReader.ensureFileBuffer(view.seqTo, ReaderContext.fillLineCount);
							if (fillIndex[1] <= fillIndex[0]) fillIndex[1] = fileReader.contentLine.length;
							
							return view.appendViewsAfter(fileReader.contentLine, fileReader.lineOffset, fillIndex[0], fillIndex[1] - fillIndex[0]);
						} else {
							int from = view.seqFrom - ReaderContext.fillLineCount;
							if (from < 0) from = 0;
							int size = view.seqFrom - from;
							int[] fillIndex = fileReader.ensureFileBuffer(from, size);
							from = fillIndex[1] - 1;
							if (fillIndex[0] < fillIndex[1]) size = fillIndex[1] - fillIndex[0];
							else size = fillIndex[1];
							
							int ret = view.appendViewsBefore(fileReader.contentLine, fileReader.lineOffset, from, size);
							publishProgress();
							updateSemaphore.acquire();
							return ret;
						}
					}
				} catch (Exception e) {
					Log.e(this.getClass().getSimpleName(), "Error append item", e);
					return 0;
				}
			}
		}

		@Override
		protected void onPostExecute(Integer c) {
			try {
				if (c == 0) {
					showError();
					return;
				}
				if (!isOpen) return;
				
				view.flushAppender();
				
				if (isFirstFill) {
					isFirstFill = false;
					view.scrollTo(0, currReadFile.scrollOffset);
					View tmp = findViewById(R.id.num9);
					int h = tmp.getHeight();
					for (int i=1; ; i++) {
						controlProcessText.setTextSize(i);
						controlProcessText.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
						if (controlProcessText.getMeasuredHeight() > h) {
							controlProcessText.setTextSize(i - 1);
							((TextView)findViewById(R.id.controlProcessMark)).setTextSize(i - 1);
							break;
						}
					}
				}
			} finally {
				synchronized(this) {
					fillTask = null;
					this.notifyAll();
				}
			}
		}
	}

	@Override
	protected void onPause() {
		super.onPause();
	}
	@Override
	protected void onStop() {
		super.onStop();
	}

	@Override
	protected void onDestroy() {
		super.onDestroy();
		close();
	}
	private void close() {
		if (!isOpen) return;
		synchronized (statusLock) {
			isOpen = false;
			currReadFile.lastReadTime = System.currentTimeMillis();
			try {
				int[] displayInfo = view.getPosition();
				currReadFile.startLineSeq = displayInfo[0];
				int[] bufferIndex = fileReader.ensureFileBuffer(currReadFile.startLineSeq, 1);
				int index = bufferIndex[0];
				currReadFile.scrollOffset = displayInfo[1];
				currReadFile.fileOffset = fileReader.lineOffset[index];
				if (60 > fileReader.contentLine[index].length()) {
					currReadFile.summary = fileReader.contentLine[index];
				} else {
					currReadFile.summary = fileReader.contentLine[index].substring(0, 60) + "……";
				}
				currReadFile.process = displayInfo[2];
			} catch (Exception e) {
				currReadFile.reset();
				Log.e(LOG, "Error updating read position", e);
			}
			fileReader = null;
			currReadFile.offer();
			currReadFile = null;
			view.removeAllViews();
			view.destroyDrawingCache();
			view = null;
		}
	}
	
	@Override
	public void onBackPressed() {
		View tmpView = findViewById(R.id.processControllPanel);
		if (tmpView.getVisibility() == View.INVISIBLE) {
			super.onBackPressed();
			exit();
		} else {
			tmpView.setVisibility(View.INVISIBLE);
		}
	}
	
	private void exit() {
		close();
		ReaderContext.save();
		finish();
	}
}
